If anyone ends up on this thread because they can't import PDFKit in a macOS app, especially because of "Undefined symbols", that's because the right way to do it is
import Quartz.PDFKit
I don't know why it's not mentioned in the documentation page - https://developer.apple.com/documentation/pdfkit/pdfview since the one - https://developer.apple.com/documentation/quartz/pdfkit talking about it seems to be a mirror. Anyway, you can checkout the WWDC17 video - https://developer.apple.com/videos/play/wwdc2017/241/ about that (first example given is with a Cocoa app).
Post
Replies
Boosts
Views
Activity
The shortcuts in Xcode 13.1 were localized: they were the same key combinations for every keyboard and language, or what made the most sense. After a few days using them, I got accustomed and really enjoyed the small boost of productivity they gave me. This was hard at first but the reward was worth it.
Now with Xcode 13.2 they took this feature back and I have to mentally remap the shortcuts to the previous one (argh). That's too bad. They may have had to revert the modification because too much people were complaining (may they be right or wrong). Now I am stuck with the previous shortcuts although I have tasted the new ones and preferred them.
Maybe the best option would be to let people choose what shortcuts they prefer? Or when to take the step to learn the new shortcuts and get a productivity gain?
YouTube is hiding the keyboard too. I wonder if Spotify and YouTube use UIKit or TVMLKit for their search tab.
When inspecting the search controller, it embeds the search results controller view inside another view and doesn't set any constraint on it. I guess they only set the frame of the containing view (the X origin is ~500 points). When the keyboard looses the focus, the containing view should have its origin updated to snap to the safe area leading edge, and the keyboard trailing edge should be set to the window leading edge. Maybe they forgot to implement this behavior.
Note: I filed a bug through Feedback Assistant two months ago but did not get any answer so far. Still open.
Thanks for taking the time to answer. I still have interrogations if I may:
it is said in the video that the keyboard will slide. Is it only for 3rd party apps?
the line keyboard does hide for 3rd party apps. Is it different from the grid keyboard?
with the grid keyboard it’s not possible to align the leading elements of a collection view with the leading edge of the search container. Not without clipping the elements when focused. Could you tell me if there is a proper solution for that ?
Hey ! I stumbled across your post while searching myself, since I found a solution, let me post it here.
Goal
To recap, I am trying to read several audio files and output them in different channels. In my setup, I have 2 devices that both have two channels so I have 4 channels in the end. I use an aggregate device and run the iOS app with Catalyst (I couldn't make aggregate device work with AVAudioEngine on macOS 🤷♂️).
I use AVAudioSourceNode as input but it should also work with AVAudioPlayerNode since both have an auAudioUnit property.
Setup formats
When the engine is being setup, I make sure that the channels count of the output node is 4:
let outputFormat = audioEngine.outputNode.outputFormat(forBus: 0)
print("Channels count:", outputFormat.channelCount) // 4
If not, this might be because the AVAudioSession is not configured with the multiRoute category.
Mixer -> Output
Then, I had to connect the engine main mixer node to the output node using the output node format.
audioEngine.connect(audioEngine.mainMixerNode, to: audioEngine.outputNode, format: format)
I don't know why it's necessary. When I read the doc, it seems that it's already done by default.
If the client never sets the connection format between the mainMixerNode and the outputNode, the engine always updates the format to track the format of the outputNode on startup or restart, even after an AVAudioEngineConfigurationChange.
Source -> Mixer
I also had to connect each source node to the main mixer node using the output format. Otherwise the channel mapping will not be effective.
engine.connect(sourceNode, to: engine.mainMixerNode, format: outputFormat)
Source channels mapping
In the end, I could set the auAudioUnit.channelMap of each source node to output to the desired channels. For instance
sourceNode.auAudioUnit.channelMap = [0, 1, -1, -1]
to output in the first pair of speakers (default) and
sourceNode.auAudioUnit.channelMap = [-1, -1, 0, 1]
to output to the second pair of speakers.
I am facing the same issue with iOS 17.5.1 although I have found a workaround.
I noticed that the status bar reset to the desired default behavior when toggling the OS preferred color scheme (light/dark mode). I tried to reproduced such a thing in code and was able to achieve similar results by returning either lightContent or darkContent for preferredStatusBarStyle when updating the status bar appearance after changing its visibility value. Then requesting again an update with the default value for preferredStatusBarStyle.
It looks something like this.
// returned by `prefersStatusBarHidden`
isStatusBarHidden = false
// returned by `preferredStatusBarStyle`
preferredStyle = .darkContent
setNeedsStatusBarAppearanceUpdate()
// here we reset to default
preferredStyle = .default
setNeedsStatusBarAppearanceUpdate()
// status bar style should be back to desired default behavior
Hoping iOS 18 will fix this issue 🤞
For what it's worth, here's my implementation to add a UILargeContentViewerInteraction to a UITabBar without using UITabBarController but inside a custom UIViewController.
Interaction
First, add the interaction to the view of the view controller with the view controller as the interaction delegate.
view.showsLargeContentViewer = true
view.scalesLargeContentImage = true
view.addInteraction(UILargeContentViewerInteraction(delegate: self))
Delegate
It's needed to implement tow function of the UILargeContentViewerInteractionDelegate protocol:
largeContentViewerInteraction(_:itemAt:)
largeContentViewerInteraction(_:didEndOn:at:)
Retrieve Item
Both functions have to get the index of the tab bar item for the point parameter so here's a function to do that:
private func tabItemIndex(for point: CGPoint) -> Int? {
let convertedPoint = view.convert(point, to: tabBar)
guard
tabBar.point(inside: convertedPoint, with: nil),
let numberOfItems = tabBar.items?.count,
numberOfItems > 0
else { return nil }
let itemWidth = tabBar.bounds.width / CGFloat(numberOfItems)
return Int(convertedPoint.x / itemWidth)
}
The function converts the point from the view to the tabBar and ensures the point is inside the tabBar. Then the point x member is divided by the width of an item so that every point targets an item.
Large Content Item
For the large content item, the view itself is used. It is configured using the retrieved UITabBarItem:
func largeContentViewerInteraction(
_: UILargeContentViewerInteraction,
itemAt point: CGPoint
) -> (any UILargeContentViewerItem)? {
guard
let tabItemIndex = tabItemIndex(for: point),
let item = tabBar.items?[tabItemIndex]
else { return nil }
view.largeContentTitle = item.title
view.largeContentImage = item.image
return view
}
Selecting Item
The item is then selected when the interaction ends.
func largeContentViewerInteraction(
_: UILargeContentViewerInteraction,
didEndOn _: (any UILargeContentViewerItem)?,
at point: CGPoint
) {
guard
let tabItemIndex = tabItemIndex(for: point),
let item = tabBar.items?[tabItemIndex]
else { return }
tabBar.selectedItem = item
tabBar(tabBar, didSelect: item) // call of UITabBarDelegate
}
I've had the same issue with Form since tvOS 17. I think it's supposed to be fixed by using the scrollClipDisabled(_:) modifier and the padding() one as explained in the doc.
Although I had to manually find a proper amount of padding on the leading edge (around 60 in my implementation) to make sure the shadow was not clipped.
Filed a feedback: FB15518900